//! Helper functions for initialising GridTrack's from styles
//! This mainly consists of evaluating GridAutoTracks
use super::types::{GridTrack, TrackCounts};
use crate::geometry::AbsoluteAxis;
use crate::style::{LengthPercentage, RepetitionCount, TrackSizingFunction};
use crate::style_helpers::TaffyAuto;
use crate::util::sys::{ceil, floor, Vec};
use crate::util::MaybeMath;
use crate::util::ResolveOrZero;
use crate::{GenericGridTemplateComponent, GenericRepetition, GridContainerStyle};

/// The auto-repeat fit strategy to use
pub(crate) enum AutoRepeatStrategy {
    /// If the grid container has a definite size or max size in the relevant axis:
    ///   - then the number of repetitions is the largest possible positive integer that does not cause the grid to overflow the content
    ///     box of its grid container.
    MaxRepetitionsThatDoNotOverflow,
    /// Otherwise, if the grid container has a definite min size in the relevant axis:
    ///   - then the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement
    MinRepetitionsThatDoOverflow,
}

/// Compute the number of rows and columns in the explicit grid
pub(crate) fn compute_explicit_grid_size_in_axis(
    style: &impl GridContainerStyle,
    auto_fit_container_size: Option<f32>,
    auto_fit_strategy: AutoRepeatStrategy,
    resolve_calc_value: impl Fn(*const (), f32) -> f32,
    axis: AbsoluteAxis,
) -> (u16, u16) {
    let template = match axis {
        AbsoluteAxis::Horizontal => style.grid_template_columns(),
        AbsoluteAxis::Vertical => style.grid_template_rows(),
    };
    let Some(template) = template else {
        return (0, 0);
    };

    // If template contains no tracks, then there are trivially zero explicit tracks
    let track_count = template.len();
    if track_count == 0 {
        return (0, 0);
    }

    // If there are any repetitions that contains no tracks, then the whole definition should be considered invalid
    // and we default to no explicit tracks
    let template_has_repetitions_with_zero_tracks = template.clone().any(|track_def| match track_def {
        GenericGridTemplateComponent::Single(_) => false,
        GenericGridTemplateComponent::Repeat(repeat) => repeat.track_count() == 0,
    });
    if template_has_repetitions_with_zero_tracks {
        return (0, 0);
    }

    // Compute that number of track generated by single track definition and repetitions with a fixed repetition count
    let non_auto_repeating_track_count = template
        .clone()
        .map(|track_def| match track_def {
            GenericGridTemplateComponent::Single(_) => 1,
            GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
                RepetitionCount::Count(count) => count * repeat.track_count(),
                RepetitionCount::AutoFit | RepetitionCount::AutoFill => 0,
            },
        })
        .sum::<u16>();

    let auto_repetition_count: u16 = template.clone().filter(|track_def| track_def.is_auto_repetition()).count() as u16;
    let all_track_defs_have_fixed_component = template.clone().all(|track_def| match track_def {
        GenericGridTemplateComponent::Single(sizing_function) => sizing_function.has_fixed_component(),
        GenericGridTemplateComponent::Repeat(repeat) => {
            repeat.tracks().all(|sizing_function| sizing_function.has_fixed_component())
        }
    });

    let template_is_valid =
        auto_repetition_count == 0 || (auto_repetition_count == 1 && all_track_defs_have_fixed_component);

    // If the template is invalid because it contains multiple auto-repetition definitions or it combines an auto-repetition
    // definition with non-fixed-size track sizing functions, then disregard it entirely and default to zero explicit tracks
    if !template_is_valid {
        return (0, 0);
    }

    // If there are no repetitions, then the number of explicit tracks is simply equal to the lengths of the track definition
    // vector (as each item in the Vec represents one track).
    if auto_repetition_count == 0 {
        return (0, non_auto_repeating_track_count);
    }

    let repetition_definition = template
        .clone()
        .find_map(|def| match def {
            GenericGridTemplateComponent::Single(_) => None,
            GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
                RepetitionCount::Count(_) => None,
                RepetitionCount::AutoFit | RepetitionCount::AutoFill => Some(repeat),
            },
        })
        .unwrap();
    let repetition_definition_iter = repetition_definition.tracks();
    let repetition_track_count = repetition_definition_iter.len() as u16;

    // Determine the number of repetitions
    let num_repetitions: u16 = match auto_fit_container_size {
        None => 1,
        Some(inner_container_size) => {
            let parent_size = Some(inner_container_size);

            /// ...treating each track as its max track sizing function if that is definite or as its minimum track sizing function
            /// otherwise, flooring the max track sizing function by the min track sizing function if both are definite
            fn track_definite_value(
                sizing_function: TrackSizingFunction,
                parent_size: Option<f32>,
                calc_resolver: impl Fn(*const (), f32) -> f32,
            ) -> f32 {
                let max_size = sizing_function.max.definite_value(parent_size, &calc_resolver);
                let min_size = sizing_function.min.definite_value(parent_size, &calc_resolver);
                max_size.map(|max| max.maybe_max(min_size)).or(min_size).unwrap()
            }

            let non_repeating_track_used_space: f32 = template
                .clone()
                .map(|track_def| match track_def {
                    GenericGridTemplateComponent::Single(sizing_function) => {
                        track_definite_value(sizing_function, parent_size, &resolve_calc_value)
                    }
                    GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
                        RepetitionCount::Count(count) => {
                            let sum = repeat
                                .tracks()
                                .map(|sizing_function| {
                                    track_definite_value(sizing_function, parent_size, &resolve_calc_value)
                                })
                                .sum::<f32>();
                            sum * (count as f32)
                        }
                        RepetitionCount::AutoFit | RepetitionCount::AutoFill => 0.0,
                    },
                })
                .sum();
            let gap_size = style.gap().get_abs(axis).resolve_or_zero(Some(inner_container_size), &resolve_calc_value);

            // Compute the amount of space that a single repetition of the repeated track list takes
            let per_repetition_track_used_space: f32 = repetition_definition_iter
                .map(|sizing_function| track_definite_value(sizing_function, parent_size, &resolve_calc_value))
                .sum::<f32>();

            // We special case the first repetition here because the number of gaps in the first repetition
            // depends on the number of non-repeating tracks in the template
            let first_repetition_and_non_repeating_tracks_used_space = non_repeating_track_used_space
                + per_repetition_track_used_space
                + ((non_auto_repeating_track_count + repetition_track_count).saturating_sub(1) as f32 * gap_size);

            // If a single repetition already overflows the container then we return 1 as the repetition count
            // (the number of repetitions is floored at 1)
            if first_repetition_and_non_repeating_tracks_used_space > inner_container_size {
                1u16
            } else {
                let per_repetition_gap_used_space = (repetition_track_count as f32) * gap_size;
                let per_repetition_used_space = per_repetition_track_used_space + per_repetition_gap_used_space;
                let num_repetition_that_fit = (inner_container_size
                    - first_repetition_and_non_repeating_tracks_used_space)
                    / per_repetition_used_space;

                // If the container size is a preferred or maximum size:
                //   Then we return the maximum number of repetitions that fit into the container without overflowing.
                // If the container size is a minimum size:
                //   - Then we return the minimum number of repetitions required to overflow the size.
                //
                // In all cases we add the additional repetition that was already accounted for in the special-case computation above
                match auto_fit_strategy {
                    AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow => (floor(num_repetition_that_fit) as u16) + 1,
                    AutoRepeatStrategy::MinRepetitionsThatDoOverflow => (ceil(num_repetition_that_fit) as u16) + 1,
                }
            }
        }
    };

    let grid_template_track_count = non_auto_repeating_track_count + (repetition_track_count * num_repetitions);
    (num_repetitions, grid_template_track_count)
}

/// Resolve the track sizing functions of explicit tracks, automatically created tracks, and gutters
/// given a set of track counts and all of the relevant styles
pub(super) fn initialize_grid_tracks(
    tracks: &mut Vec<GridTrack>,
    counts: TrackCounts,
    style: &impl GridContainerStyle,
    axis: AbsoluteAxis,
    track_has_items: impl Fn(usize) -> bool,
) {
    // Extract styles
    let track_template;
    let auto_tracks;
    let gap;
    match axis {
        AbsoluteAxis::Horizontal => {
            track_template = style.grid_template_columns();
            auto_tracks = style.grid_auto_columns();
            gap = style.gap().width;
        }
        AbsoluteAxis::Vertical => {
            track_template = style.grid_template_rows();
            auto_tracks = style.grid_auto_rows();
            gap = style.gap().height;
        }
    };

    // Clear vector (in case this is a re-layout), reserve space for all tracks ahead of time to reduce allocations,
    // and push the initial gutter
    tracks.clear();
    tracks.reserve((counts.len() * 2) + 1);
    tracks.push(GridTrack::gutter(gap));

    let auto_track_count = auto_tracks.len();

    // Create negative implicit tracks
    if counts.negative_implicit > 0 {
        if auto_track_count == 0 {
            let iter = core::iter::repeat(TrackSizingFunction::AUTO);
            create_implicit_tracks(tracks, counts.negative_implicit, iter, gap)
        } else {
            let offset = auto_track_count - (counts.negative_implicit as usize % auto_track_count);
            let iter = auto_tracks.clone().cycle().skip(offset);
            create_implicit_tracks(tracks, counts.negative_implicit, iter, gap)
        }
    }

    let mut current_track_index = (counts.negative_implicit) as usize;

    // Create explicit tracks
    // An explicit check against the count (rather than just relying on track_template being empty) is required here
    // because a count of zero can result from the track_template being invalid, in which case it should be ignored.
    if counts.explicit > 0 {
        if let Some(track_template) = track_template {
            track_template.clone().for_each(|track_sizing_function| {
                match track_sizing_function {
                    GenericGridTemplateComponent::Single(sizing_function) => {
                        tracks.push(GridTrack::new(
                            sizing_function.min_sizing_function(),
                            sizing_function.max_sizing_function(),
                        ));
                        tracks.push(GridTrack::gutter(gap));
                        current_track_index += 1;
                    }
                    GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
                        RepetitionCount::Count(count) => {
                            let track_iter = repeat.tracks();
                            let track_iter = track_iter.cycle().take(repeat.track_count() as usize * count as usize);
                            track_iter.for_each(|sizing_function| {
                                tracks.push(GridTrack::new(
                                    sizing_function.min_sizing_function(),
                                    sizing_function.max_sizing_function(),
                                ));
                                tracks.push(GridTrack::gutter(gap));
                                current_track_index += 1;
                            });
                        }
                        RepetitionCount::AutoFit | RepetitionCount::AutoFill => {
                            let auto_repeated_track_count =
                                (counts.explicit - (track_template.len() as u16 - 1)) as usize;
                            let iter = repeat.tracks().cycle();
                            for track_def in iter.take(auto_repeated_track_count) {
                                let mut track =
                                    GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function());
                                let mut gutter = GridTrack::gutter(gap);

                                // Auto-fit tracks that don't contain should be collapsed.
                                if repeat.count() == RepetitionCount::AutoFit && !track_has_items(current_track_index) {
                                    track.collapse();
                                    gutter.collapse();
                                }

                                tracks.push(track);
                                tracks.push(gutter);

                                current_track_index += 1;
                            }
                        }
                    },
                }
            });
        }
    }

    let grid_area_tracks = (counts.negative_implicit + counts.explicit) - current_track_index as u16;

    // Create positive implicit tracks
    if auto_track_count == 0 {
        let iter = core::iter::repeat(TrackSizingFunction::AUTO);
        create_implicit_tracks(tracks, counts.positive_implicit + grid_area_tracks, iter, gap)
    } else {
        let iter = auto_tracks.clone().cycle();
        create_implicit_tracks(tracks, counts.positive_implicit + grid_area_tracks, iter, gap)
    }

    // Mark first and last grid lines as collapsed
    tracks.first_mut().unwrap().collapse();
    tracks.last_mut().unwrap().collapse();
}

/// Utility function for repeating logic of creating implicit tracks
fn create_implicit_tracks(
    tracks: &mut Vec<GridTrack>,
    count: u16,
    mut auto_tracks_iter: impl Iterator<Item = TrackSizingFunction>,
    gap: LengthPercentage,
) {
    for _ in 0..count {
        let track_def = auto_tracks_iter.next().unwrap();
        tracks.push(GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function()));
        tracks.push(GridTrack::gutter(gap));
    }
}

#[cfg(test)]
mod test {
    use super::compute_explicit_grid_size_in_axis;
    use super::initialize_grid_tracks;
    use crate::compute::grid::explicit_grid::AutoRepeatStrategy;
    use crate::compute::grid::types::GridTrackKind;
    use crate::compute::grid::types::TrackCounts;
    use crate::compute::grid::util::*;
    use crate::geometry::AbsoluteAxis;
    use crate::prelude::*;
    use crate::sys::DefaultCheapStr;

    #[test]
    fn explicit_grid_sizing_no_repeats() {
        let grid_style = (600.0, 600.0, 2, 4).into_grid();
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 2);
        assert_eq!(row_count, 4);
        assert_eq!(auto_col_reps, 0);
        assert_eq!(auto_row_reps, 0);
    }

    #[test]
    fn explicit_grid_sizing_auto_fill_exact_fit() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            size: Size { width: length(120.0), height: length(80.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            ..Default::default()
        };
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 3);
        assert_eq!(row_count, 4);
        assert_eq!(auto_col_reps, 3);
        assert_eq!(auto_row_reps, 4);
    }

    #[test]
    fn explicit_grid_sizing_auto_fill_non_exact_fit() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            size: Size { width: length(140.0), height: length(90.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            ..Default::default()
        };
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 3);
        assert_eq!(row_count, 4);
        assert_eq!(auto_col_reps, 3);
        assert_eq!(auto_row_reps, 4);
    }

    #[test]
    fn explicit_grid_sizing_auto_fill_min_size_exact_fit() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            min_size: Size { width: length(120.0), height: length(80.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            ..Default::default()
        };
        let inner_container_size = Size { width: Some(120.0), height: Some(80.0) };
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            inner_container_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            inner_container_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 3);
        assert_eq!(row_count, 4);
        assert_eq!(auto_col_reps, 3);
        assert_eq!(auto_row_reps, 4);
    }

    #[test]
    fn explicit_grid_sizing_auto_fill_min_size_non_exact_fit() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            min_size: Size { width: length(140.0), height: length(90.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            ..Default::default()
        };
        let inner_container_size = Size { width: Some(140.0), height: Some(90.0) };
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            inner_container_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            inner_container_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 4);
        assert_eq!(row_count, 5);
        assert_eq!(auto_col_reps, 4);
        assert_eq!(auto_row_reps, 5);
    }

    #[test]
    fn explicit_grid_sizing_auto_fill_multiple_repeated_tracks() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            size: Size { width: length(140.0), height: length(100.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), length(20.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0), length(10.0)])],
            ..Default::default()
        };
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 4); // 2 repetitions * 2 repeated tracks = 4 tracks in total
        assert_eq!(row_count, 6); // 3 repetitions * 2 repeated tracks = 4 tracks in total
        assert_eq!(auto_col_reps, 2);
        assert_eq!(auto_row_reps, 3);
    }

    #[test]
    fn explicit_grid_sizing_auto_fill_gap() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            size: Size { width: length(140.0), height: length(100.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            gap: length(20.0),
            ..Default::default()
        };
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 2); // 2 tracks + 1 gap
        assert_eq!(row_count, 3); // 3 tracks + 2 gaps
        assert_eq!(auto_col_reps, 2);
        assert_eq!(auto_row_reps, 3);
    }

    #[test]
    fn explicit_grid_sizing_no_defined_size() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), percent(0.5), length(20.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            gap: length(20.0),
            ..Default::default()
        };
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MinRepetitionsThatDoOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 3);
        assert_eq!(row_count, 1);
        assert_eq!(auto_col_reps, 1);
        assert_eq!(auto_row_reps, 1);
    }

    #[test]
    fn explicit_grid_sizing_mix_repeated_and_non_repeated() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            size: Size { width: length(140.0), height: length(100.0) },
            grid_template_columns: vec![length(20.0), repeat(AutoFill, vec![length(40.0)])],
            grid_template_rows: vec![length(40.0), repeat(AutoFill, vec![length(20.0)])],
            gap: length(20.0),
            ..Default::default()
        };
        let preferred_size = grid_style.size.map(|s| s.into_option());
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            preferred_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 3); // 3 tracks + 2 gaps
        assert_eq!(row_count, 2); // 2 tracks + 1 gap
        assert_eq!(auto_col_reps, 2);
        assert_eq!(auto_row_reps, 1);
    }

    #[test]
    fn explicit_grid_sizing_mix_with_padding() {
        use RepetitionCount::AutoFill;
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            size: Size { width: length(120.0), height: length(120.0) },
            padding: Rect { left: length(10.0), right: length(10.0), top: length(20.0), bottom: length(20.0) },
            grid_template_columns: vec![repeat(AutoFill, vec![length(20.0)])],
            grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
            ..Default::default()
        };
        let inner_container_size = Size { width: Some(100.0), height: Some(80.0) };
        let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            inner_container_size.get_abs(AbsoluteAxis::Horizontal),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Horizontal,
        );
        let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis(
            &grid_style,
            inner_container_size.get_abs(AbsoluteAxis::Vertical),
            AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow,
            |_, _| 42.42,
            AbsoluteAxis::Vertical,
        );
        assert_eq!(col_count, 5); // 40px horizontal padding
        assert_eq!(row_count, 4); // 20px vertical padding
        assert_eq!(auto_col_reps, 5);
        assert_eq!(auto_row_reps, 4);
    }

    #[test]
    fn test_initialize_grid_tracks() {
        let minpx0 = MinTrackSizingFunction::from_length(0.0);
        let minpx20 = MinTrackSizingFunction::from_length(20.0);
        let minpx100 = MinTrackSizingFunction::from_length(100.0);

        let maxpx0 = MaxTrackSizingFunction::from_length(0.0);
        let maxpx20 = MaxTrackSizingFunction::from_length(20.0);
        let maxpx100 = MaxTrackSizingFunction::from_length(100.0);

        // Setup test
        let grid_style: Style<DefaultCheapStr> = Style {
            display: Display::Grid,
            gap: length(20.0),
            grid_template_columns: vec![length(100.0), minmax(length(100.0), fr(2.0)), fr(1.0)],
            grid_auto_columns: vec![auto(), length(100.0)],
            ..Default::default()
        };
        let track_counts = TrackCounts {
            negative_implicit: 3,
            explicit: grid_style.grid_template_columns.len() as u16,
            positive_implicit: 3,
        };

        // Call function
        let mut tracks = Vec::new();
        initialize_grid_tracks(&mut tracks, track_counts, &grid_style, AbsoluteAxis::Horizontal, |_| false);

        // Assertions
        let expected = vec![
            // Gutter
            (GridTrackKind::Gutter, minpx0, maxpx0),
            // Negative implicit tracks
            (GridTrackKind::Track, minpx100, maxpx100),
            (GridTrackKind::Gutter, minpx20, maxpx20),
            (GridTrackKind::Track, auto(), auto()),
            (GridTrackKind::Gutter, minpx20, maxpx20),
            (GridTrackKind::Track, minpx100, maxpx100),
            (GridTrackKind::Gutter, minpx20, maxpx20),
            // Explicit tracks
            (GridTrackKind::Track, minpx100, maxpx100),
            (GridTrackKind::Gutter, minpx20, maxpx20),
            (GridTrackKind::Track, minpx100, MaxTrackSizingFunction::from_fr(2.0)), // Note: separate min-max functions
            (GridTrackKind::Gutter, minpx20, maxpx20),
            (GridTrackKind::Track, auto(), MaxTrackSizingFunction::from_fr(1.0)), // Note: min sizing function of flex sizing functions is AUTO
            (GridTrackKind::Gutter, minpx20, maxpx20),
            // Positive implicit tracks
            (GridTrackKind::Track, auto(), auto()),
            (GridTrackKind::Gutter, minpx20, maxpx20),
            (GridTrackKind::Track, minpx100, maxpx100),
            (GridTrackKind::Gutter, minpx20, maxpx20),
            (GridTrackKind::Track, auto(), auto()),
            (GridTrackKind::Gutter, minpx0, maxpx0),
        ];

        assert_eq!(tracks.len(), expected.len(), "Number of tracks doesn't match");

        for (idx, (actual, (kind, min, max))) in tracks.into_iter().zip(expected).enumerate() {
            assert_eq!(actual.kind, kind, "Track {idx} (0-based index)");
            assert_eq!(actual.min_track_sizing_function, min, "Track {idx} (0-based index)");
            assert_eq!(actual.max_track_sizing_function, max, "Track {idx} (0-based index)");
        }
    }
}
